#include"sys/Memory.h"
#include"NOW.h"
#include"sys/TLB.h"
#include"sys/IO.h"
// #define CONSIDER_USER_MEM_RANGE

static void Log(const char *format, ...){
    va_list args;
    va_start(args, format);          // 初始化va_list
    vfprintf(stdout,format, args);  // 将参数传递给vfprintf
    va_end(args);   
}
static void LogErr(const char *format, ...) {
    va_list args;
    va_start(args, format);          // 初始化va_list
    vfprintf(stderr, format, args);  // 将参数传递给vfprintf
    va_end(args);                    // 清理va_list
}
int get_latest_PTE(int pid, unsigned vpn, PageTableEntry *pte, int T){
    if (TLB_find(pid,vpn,pte,T)){
        return 1;
    }
    Process *proc = &jobs[pid];
    if (proc->id ==-1 || !proc->PTB){
        return 0;
    }
    *pte = proc->PTB->e[vpn];
    return 1;
}
//0表示访问失败（内存中没有），1访问成功，2权限不足
static int get_PPN_from_Mem(int pid, unsigned vpn,short ACL, unsigned* pPPN, int *pT){//需保证访问的VPN是合法的
    //查询TLB
    PageTableEntry entry;
    int T= *pT;
    T+=TLB_ACCESS_TIME;
    if (TLB_find(pid, vpn, &entry, T)){
        //查询页表项成功
        Log("[%d] TLB find entry successfully!\n",T);
        goto find_entry;
    }
    Log("[%d] TLB find entry miss!\n",T);
    //查询TLB失败。查页表。
    T+=MEM_ACCESS_TIME;
    Process *proc = &jobs[pid];
    entry = (proc->PTB)->e[vpn];
    if (entry.v){
        //查询页表成功
        //回写TLB
        Log("[%d] Memory page table find entry successfully!\n",T);
        TLB_add(pid,vpn,entry,T);
        goto find_entry;
    }
    //无法从内存中获取页表项
    *pT = T;
    return 0;
find_entry:
    if ((ACL & entry.access) == ACL){//ACL 包含于 entry.access，允许访问
        *pPPN = entry.PPN;
        PR_UpdateFrame(entry.PPN, T);
        // *pT = T;
        return 1;
    }else{
        Log("[%d] Insufficent previlieges!You have %hd, access enable %hd.\n",T,ACL,entry.access);
        return 2;
    }
}

int get_physical_address(int pid, unsigned VA,short ACL, unsigned* pPA, int T){
    unsigned vpn = VA/PAGE_SIZE;
    unsigned offset = VA%PAGE_SIZE;
    Process *proc = &jobs[pid];
    _Bool outOfRange = vpn >= PHY_PAGE_NUM;
    ++proc->page_find_cnt;
    #ifdef CONSIDER_USER_MEM_RANGE
        outOfRange |= vpn< proc->sp || vpn> proc->hp;
    #endif
    if (outOfRange) {
        LogErr("[%d] Segment fault: Virtual address %#x out of range.\n",T,VA);
        return 0;
    }
    //从内存中获取PPN
    unsigned ppn;
    int res = get_PPN_from_Mem(pid,vpn,ACL,&ppn,&T);
    if (res==1){
        *pPA = ppn*PAGE_SIZE+offset;
        return 1;
    }else if (res ==2)
        return 0;
    //无法从内存中获取。
    ++proc->page_fault_cnt;
    Log("[%d] pageFault!\n",T);
    //查询PT失败，pageFault 
    //……
    T+=100;//假设pageFault所需
    //重新访问内存
    res = get_PPN_from_Mem(pid,vpn,ACL,&ppn,&T);
    if (res==1){
        *pPA = ppn*PAGE_SIZE+offset;
        return 1;
    }else if (res ==2)
        return 0;
    LogErr("[%d] Fatal error:useless pageFault! Too small memory!\n",T);
    return 0;
}

